package com.zeng.bean.restmodel;

import com.zeng.annotation.restmodel.RestModelField;
import com.zeng.util.GenUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.security.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 表字段信息类
 */
@Data
@Slf4j
public class RestTableFieldInfo {

    private String name;
    private String type;
    private String comment;
    private boolean nullable = true;
    private boolean primaryKey = false;
    private boolean autoIncrement = false;
    private boolean unique = false;

    private Class<?> clazz;

    /**
     * 通过表类型，获取字段信息
     */
    public static List<RestTableFieldInfo> loadByTableClass(Class<?> clazz) {
        List<RestTableFieldInfo> infos = new ArrayList<>();
        Field[] d1 = clazz.getSuperclass().getDeclaredFields();
        Field[] d2 = clazz.getDeclaredFields();
        List<Field> declaredFields = new ArrayList<>();
        declaredFields.addAll(Arrays.asList(d1));
        declaredFields.addAll(Arrays.asList(d2));

        for (Field declaredField : declaredFields) {
            RestTableFieldInfo filed = new RestTableFieldInfo();
            Class<?> filedClass = declaredField.getType();
            filed.setClazz(filedClass);

            String filedName = declaredField.getName();
            String dbType = getDBFiledType(filedClass);

            if(dbType == null) {
                log.info("类{}的字段{}({})无法找到对应的数据库类型",clazz.getName(),filedName,filedClass.getName());
                continue;
            }

            RestModelField annotation = declaredField.getAnnotation(RestModelField.class);

            // region 无注解配置

            if (annotation == null) {

                filed.setName(filedName);
                filed.setType(dbType);

                infos.add(filed);
                continue;
            }

            // endregion

            // region 读取注解配置

            if(annotation.ignore())continue;

            String atype = annotation.type();
            String aname = annotation.name();

            filed.setName(GenUtils.stringIsNullOrWhiteSpace(aname) ? filedName : aname);
            filed.setType(GenUtils.stringIsNullOrWhiteSpace(atype) ? dbType : atype);
            filed.setComment(annotation.comment());
            filed.setNullable(annotation.nullable());
            filed.setPrimaryKey(annotation.primaryKey());
            filed.setAutoIncrement(annotation.autoIncrement());
            filed.setUnique(annotation.unique());

            infos.add(filed);

            // endregion
        }

        return infos;
    }

    /**
     * 获取类在数据库中的字段类型
     * @param clazz
     * @return
     */
    public static String getDBFiledType(Class<?> clazz) {
        if(clazz == int.class || clazz == Integer.class) return "INT";
        if(clazz == long.class || clazz == Long.class) return "BIGINT";
        if(clazz == float.class || clazz == Float.class) return "FLOAT";
        if(clazz == double.class || clazz == Double.class) return "DOUBLE";
        if(clazz == boolean.class || clazz == Boolean.class) return "BIT";
        if(clazz == byte.class || clazz == Byte.class) return "TINYINT";
        if(clazz == short.class || clazz == Short.class) return "SMALLINT";
        if(clazz == char.class || clazz == Character.class) return "CHAR(5)";
        if(clazz == String.class) return "VARCHAR(500)";
        if(clazz == LocalDate.class) return "DATE";
        if(clazz == LocalDateTime.class || clazz == Date.class) return "DATETIME";
        if(clazz == Timestamp.class) return "TIMESTAMP";

        return null;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof RestTableFieldInfo that)) return false;
        return nullable == that.nullable && primaryKey == that.primaryKey && autoIncrement == that.autoIncrement && Objects.equals(name, that.name) && Objects.equals(type, that.type) && Objects.equals(comment, that.comment);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, type, comment, nullable, primaryKey, autoIncrement);
    }
}
